Load libraries

library(Seurat)
library(scrattch.hicat)
library(FateID)
library(Matrix)
library(dplyr)
library(RColorBrewer)
library(ggplot2)
library(ggExtra)
library(cowplot)
library(reticulate)
library(wesanderson)
library(princurve)
use_python("/usr/bin/python3")

#Set ggplot theme as classic
theme_set(theme_classic())

Load the raw counts matrix

Countdata <- Read10X("../../RawData/Gmnc_KO/outs/filtered_feature_bc_matrix/")

Raw.data <- CreateSeuratObject(counts = Countdata,
                              project = "Gmnc_KO",
                              min.cells = 3,
                              min.features = 800)

Raw.data$Barcodes <- rownames(Raw.data@meta.data)

rm(Countdata)

dim(Raw.data)
## [1] 21077 16272
Raw.data$percent.mito <- PercentageFeatureSet(Raw.data, pattern = "^mt-")
Raw.data$percent.ribo <- PercentageFeatureSet(Raw.data, pattern = "(^Rpl|^Rps|^Mrp)")
VlnPlot(object = Raw.data, features = c("nFeature_RNA","nCount_RNA", "percent.mito", "percent.ribo"), ncol= 2) & NoAxes()

# Inspect cell based on relation between nUMI and nGene detected

# Relation between nUMI and nGene detected
Cell.QC.Stat <- Raw.data@meta.data

p1 <- ggplot(Cell.QC.Stat, aes(x=nCount_RNA, y=nFeature_RNA)) + geom_point() + geom_smooth(method="lm")
p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nCount_RNA), y=log10(nFeature_RNA))) + geom_point() + geom_smooth(method="lm")
p2 <- ggMarginal(p2, type = "histogram", fill="lightgrey")

plot_grid(plotlist = list(p1,p2), ncol=2, align='h', rel_widths = c(1, 1)) ; rm(p1,p2)

Cells with deviating nGene/nUMI ratio display an Erythrocyte signature

Raw.data <- AddModuleScore(Raw.data,
                           features = list(c("Hbb-bt", "Hbq1a", "Isg20", "Fech", "Snca", "Rec114")),
                           ctrl = 10,
                           name = "Erythrocyte.signature")

Cell.QC.Stat$Erythrocyte.signature <- Raw.data$Erythrocyte.signature1
gradient <- colorRampPalette(brewer.pal(n =11, name = "Spectral"))(100)

p1 <- ggplot(Cell.QC.Stat, aes(log10(nCount_RNA), y=log10(nFeature_RNA))) +
      geom_point(aes(color= Erythrocyte.signature))  + 
      scale_color_gradientn(colours=rev(gradient), name='Erythrocyte score') + theme(legend.position="none")

p2 <- ggplot(Cell.QC.Stat, aes(log10(nCount_RNA), y=log10(nFeature_RNA))) +
      geom_point(aes(color= percent.mito))  + 
      scale_color_gradientn(colours=rev(gradient), name='Percent mito') + theme(legend.position="none")

p3 <- ggplot(Cell.QC.Stat, aes(log10(nCount_RNA), y=log10(nFeature_RNA))) +
      geom_point(aes(color= percent.ribo))  + 
      scale_color_gradientn(colours=rev(gradient), name='Percent ribo') + theme(legend.position="none")

p1 + p2 + p3

## Exclude Erythrocytes

Cell.QC.Stat$Erythrocyte <- ifelse(Cell.QC.Stat$Erythrocyte.signature > 0.1, "Erythrocyte", "Not_Erythrocyte")
p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nCount_RNA), y=log10(nFeature_RNA))) +
  geom_point(aes(colour = Erythrocyte)) +
  theme(legend.position="none")

ggMarginal(p2, type = "histogram", fill="lightgrey")

# Filter cells based on these thresholds
Cell.QC.Stat <- Cell.QC.Stat %>% filter(Cell.QC.Stat$Erythrocyte.signature < 0.1)

Low quality cell filtering

Filtering cells based on percentage of mitochondrial transcripts

We applied a high and low median absolute deviation (mad) thresholds to exclude outlier cells

max.mito.thr <- median(Cell.QC.Stat$percent.mito) + 3*mad(Cell.QC.Stat$percent.mito)
min.mito.thr <- median(Cell.QC.Stat$percent.mito) - 3*mad(Cell.QC.Stat$percent.mito)
p1 <- ggplot(Cell.QC.Stat, aes(x=nFeature_RNA, y=percent.mito)) +
  geom_point() +
  geom_hline(aes(yintercept = max.mito.thr), colour = "red", linetype = 2) +
  geom_hline(aes(yintercept = min.mito.thr), colour = "red", linetype = 2) +
  annotate(geom = "text", label = paste0(as.numeric(table(Cell.QC.Stat$percent.mito > max.mito.thr | Cell.QC.Stat$percent.mito < min.mito.thr)[2])," cells removed\n",
                                         as.numeric(table(Cell.QC.Stat$percent.mito > max.mito.thr | Cell.QC.Stat$percent.mito < min.mito.thr)[1])," cells remain"),
           x = 6000, y = 20)

ggMarginal(p1, type = "histogram", fill="lightgrey", bins=100) 

# Filter cells based on these thresholds
Cell.QC.Stat <- Cell.QC.Stat %>% filter(percent.mito < max.mito.thr) %>% filter(percent.mito > min.mito.thr)

Filtering cells based on number of genes and transcripts detected

Remove cells with to few gene detected or with to many UMI counts

We filter cells which are likely to be doublet based on their higher content of transcript detected as well as cell with to few genes/UMI sequenced

# Set low and hight thresholds on the number of detected genes based on the one obtain with the WT dataset
min.Genes.thr <- log10(1635)
max.Genes.thr <- log10(8069)

# Set hight threshold on the number of transcripts
max.nUMI.thr <- log10(58958)
# Gene/UMI scatter plot before filtering
p1 <- ggplot(Cell.QC.Stat, aes(x=log10(nCount_RNA), y=log10(nFeature_RNA))) +
  geom_point() +
  geom_smooth(method="lm") +
  geom_hline(aes(yintercept = min.Genes.thr), colour = "green", linetype = 2) +
  geom_hline(aes(yintercept = max.Genes.thr), colour = "green", linetype = 2) +
  geom_vline(aes(xintercept = max.nUMI.thr), colour = "red", linetype = 2)

ggMarginal(p1, type = "histogram", fill="lightgrey")

# Filter cells base on both metrics
Cell.QC.Stat <- Cell.QC.Stat %>% filter(log10(nFeature_RNA) > min.Genes.thr) %>% filter(log10(nCount_RNA) < max.nUMI.thr)

Filter cells below the main population nUMI/nGene relationship

lm.model <- lm(data = Cell.QC.Stat, formula = log10(nFeature_RNA) ~ log10(nCount_RNA))

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nCount_RNA), y=log10(nFeature_RNA))) +
  geom_point() +
  geom_smooth(method="lm") +
  geom_hline(aes(yintercept = min.Genes.thr), colour = "green", linetype = 2) +
  geom_hline(aes(yintercept = max.Genes.thr), colour = "green", linetype = 2) +
  geom_vline(aes(xintercept = max.nUMI.thr), colour = "red", linetype = 2) +
  annotate(geom = "text", label = paste0(dim(Cell.QC.Stat)[1], " QC passed cells"), x = 4, y = 3.8)

ggMarginal(p2, type = "histogram", fill="lightgrey")

## Filter the Seurat object

Raw.data <- subset(x = Raw.data, subset = Barcodes %in%  Cell.QC.Stat$Barcodes)
# Plot final QC metrics
VlnPlot(object = Raw.data, features = c("nFeature_RNA","nCount_RNA", "percent.mito", "percent.ribo"), ncol= 2) & NoAxes()

p1 <- ggplot(Raw.data@meta.data, aes(x=log10(nCount_RNA), y=log10(nFeature_RNA))) + geom_point() + geom_smooth(method="lm")
ggMarginal(p1, type = "histogram", fill="lightgrey")

rm(list = ls()[!ls() %in% "Raw.data"])

Use Scrublet to detect obvious doublets

Run Scrublet with default parameter

Export raw count matrix as input to Scrublet

#Export filtered matrix
dir.create("../../RawData/Gmnc_KO/Scrublet_inputs")

exprData <- Matrix(as.matrix(Raw.data@assays[["RNA"]]@counts), sparse = TRUE)
writeMM(exprData, "../../RawData/Gmnc_KO/Scrublet_inputs/matrix1.mtx")
## NULL
import scrublet as scr
import scipy.io
import numpy as np
import os

#Load raw counts matrix and gene list
input_dir = '../../RawData/Gmnc_KO/Scrublet_inputs'
counts_matrix = scipy.io.mmread(input_dir + '/matrix1.mtx').T.tocsc()

#Initialize Scrublet
scrub = scr.Scrublet(counts_matrix,
                     expected_doublet_rate=0.1,
                     sim_doublet_ratio=2,
                     n_neighbors = 8)

#Run the default pipeline
doublet_scores, predicted_doublets = scrub.scrub_doublets(min_counts=1, 
                                                          min_cells=3, 
                                                          min_gene_variability_pctl=85, 
                                                          n_prin_comps=25)
## Preprocessing...
## Simulating doublets...
## Embedding transcriptomes using PCA...
## Calculating doublet scores...
## Automatically set threshold at doublet score = 0.35
## Detected doublet rate = 3.7%
## Estimated detectable doublet fraction = 26.0%
## Overall doublet rate:
##  Expected   = 10.0%
##  Estimated  = 14.2%
## Elapsed time: 20.1 seconds
# Import scrublet's doublet score
Raw.data$Doubletscore <- py$doublet_scores

# Plot doublet score
ggplot(Raw.data@meta.data, aes(x = Doubletscore, stat(ndensity))) +
  geom_histogram(bins = 200, colour ="lightgrey")+
  geom_vline(xintercept = 0.15, colour = "red", linetype = 2)

# Manually set threshold at doublet score to 0.2
Raw.data$Predicted_doublets <- ifelse(py$doublet_scores > 0.15, "Doublet","Singlet")
table(Raw.data$Predicted_doublets)
## 
## Doublet Singlet 
##    2273   10215
Raw.data <- subset(x = Raw.data, subset = Predicted_doublets == "Singlet")

Generate SRING dimentionality reduction

Export counts matrix

dir.create("./SpringCoordinates")
# Export raw expression matrix and gene list to regenerate a spring plot
exprData <- Matrix(as.matrix(Raw.data@assays[["RNA"]]@counts), sparse = TRUE)
writeMM(exprData, "./SpringCoordinates/ExprData.mtx")
## NULL
Genelist <- row.names(Raw.data@assays[["RNA"]]@counts)
write.table(Genelist, "./SpringCoordinates/Genelist.csv", sep="\t", col.names = F, row.names = F, quote = F)
#Export metadata
Scrublet <- c("Scrublet", Raw.data$Predicted_doublets)
Scrublet <- paste(Scrublet, sep=",", collapse=",")

Cellgrouping <- Scrublet
write.table(Cellgrouping, "./SpringCoordinates/Cellgrouping.csv", quote =F, row.names = F, col.names = F)

Import coordinates

spring.coor <- read.table("SpringCoordinates/coordinates.txt", sep = ",", header = F, row.names = 1)
colnames(spring.coor) <- c("Spring_1", "Spring_2")
Spring.Sym <- function(x){
  x = abs(max(spring.coor$Spring_2)-x)
 }

spring.coor$Spring_2 <- sapply(spring.coor$Spring_2, function(x) Spring.Sym(x))
Raw.data$Spring_1 <- spring.coor$Spring_1
Raw.data$Spring_2 <- spring.coor$Spring_2
spring <- as.matrix(Raw.data@meta.data %>% select("Spring_1", "Spring_2"))
  
Raw.data[["spring"]] <- CreateDimReducObject(embeddings = spring, key = "Spring_", assay = DefaultAssay(Raw.data))
DimPlot(Raw.data, 
        reduction = "spring",
        pt.size = 0.5) & NoAxes()

# Broad clustering

Sctransform normalization

Raw.data <- SCTransform(Raw.data,
                        method = "glmGamPoi",
                        vars.to.regress = c("percent.mito"),
                        verbose = T)
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |==================                                                    |  25%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |====================================================                  |  75%
  |                                                                            
  |======================================================================| 100%
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |==                                                                    |   3%
  |                                                                            
  |====                                                                  |   5%
  |                                                                            
  |=====                                                                 |   8%
  |                                                                            
  |=======                                                               |  10%
  |                                                                            
  |=========                                                             |  13%
  |                                                                            
  |===========                                                           |  15%
  |                                                                            
  |=============                                                         |  18%
  |                                                                            
  |==============                                                        |  21%
  |                                                                            
  |================                                                      |  23%
  |                                                                            
  |==================                                                    |  26%
  |                                                                            
  |====================                                                  |  28%
  |                                                                            
  |======================                                                |  31%
  |                                                                            
  |=======================                                               |  33%
  |                                                                            
  |=========================                                             |  36%
  |                                                                            
  |===========================                                           |  38%
  |                                                                            
  |=============================                                         |  41%
  |                                                                            
  |===============================                                       |  44%
  |                                                                            
  |================================                                      |  46%
  |                                                                            
  |==================================                                    |  49%
  |                                                                            
  |====================================                                  |  51%
  |                                                                            
  |======================================                                |  54%
  |                                                                            
  |=======================================                               |  56%
  |                                                                            
  |=========================================                             |  59%
  |                                                                            
  |===========================================                           |  62%
  |                                                                            
  |=============================================                         |  64%
  |                                                                            
  |===============================================                       |  67%
  |                                                                            
  |================================================                      |  69%
  |                                                                            
  |==================================================                    |  72%
  |                                                                            
  |====================================================                  |  74%
  |                                                                            
  |======================================================                |  77%
  |                                                                            
  |========================================================              |  79%
  |                                                                            
  |=========================================================             |  82%
  |                                                                            
  |===========================================================           |  85%
  |                                                                            
  |=============================================================         |  87%
  |                                                                            
  |===============================================================       |  90%
  |                                                                            
  |=================================================================     |  92%
  |                                                                            
  |==================================================================    |  95%
  |                                                                            
  |====================================================================  |  97%
  |                                                                            
  |======================================================================| 100%
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |==                                                                    |   3%
  |                                                                            
  |====                                                                  |   5%
  |                                                                            
  |=====                                                                 |   8%
  |                                                                            
  |=======                                                               |  10%
  |                                                                            
  |=========                                                             |  13%
  |                                                                            
  |===========                                                           |  15%
  |                                                                            
  |=============                                                         |  18%
  |                                                                            
  |==============                                                        |  21%
  |                                                                            
  |================                                                      |  23%
  |                                                                            
  |==================                                                    |  26%
  |                                                                            
  |====================                                                  |  28%
  |                                                                            
  |======================                                                |  31%
  |                                                                            
  |=======================                                               |  33%
  |                                                                            
  |=========================                                             |  36%
  |                                                                            
  |===========================                                           |  38%
  |                                                                            
  |=============================                                         |  41%
  |                                                                            
  |===============================                                       |  44%
  |                                                                            
  |================================                                      |  46%
  |                                                                            
  |==================================                                    |  49%
  |                                                                            
  |====================================                                  |  51%
  |                                                                            
  |======================================                                |  54%
  |                                                                            
  |=======================================                               |  56%
  |                                                                            
  |=========================================                             |  59%
  |                                                                            
  |===========================================                           |  62%
  |                                                                            
  |=============================================                         |  64%
  |                                                                            
  |===============================================                       |  67%
  |                                                                            
  |================================================                      |  69%
  |                                                                            
  |==================================================                    |  72%
  |                                                                            
  |====================================================                  |  74%
  |                                                                            
  |======================================================                |  77%
  |                                                                            
  |========================================================              |  79%
  |                                                                            
  |=========================================================             |  82%
  |                                                                            
  |===========================================================           |  85%
  |                                                                            
  |=============================================================         |  87%
  |                                                                            
  |===============================================================       |  90%
  |                                                                            
  |=================================================================     |  92%
  |                                                                            
  |==================================================================    |  95%
  |                                                                            
  |====================================================================  |  97%
  |                                                                            
  |======================================================================| 100%

Run PCA and broad clustering

Raw.data <- RunPCA(Raw.data, verbose = FALSE)

Raw.data <- FindNeighbors(Raw.data,
                          dims = 1:20,
                          k.param = 8)

Raw.data <- FindClusters(Raw.data, resolution = 0.2)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 10215
## Number of edges: 218490
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.9335
## Number of communities: 9
## Elapsed time: 1 seconds
DimPlot(Raw.data,
        reduction = "spring",
        cols = c("#ebcb2e", "#9ec22f", "#a9961b", "#cc3a1b", "#d14c8d", "#4cabdc", "#5ab793", "#e7823a", "#046c9a", "#4990c9"),
        pt.size = 0.5) & NoAxes()

Raw.data$Broadclust.ident <- Raw.data$seurat_clusters

Differentiating neurons sub-clustering

Extract differentiating neurons

Neurons.data <-  subset(Raw.data, idents = 3)

DimPlot(Neurons.data,
        reduction = "spring",
        pt.size = 1,
        cols =  c("#cc3a1b")) + NoAxes()

## Fit pseudotime

fit <- principal_curve(as.matrix(Neurons.data@meta.data[,c("Spring_1", "Spring_2")]),
                       smoother='lowess',
                       trace=TRUE,
                       f = 1,
                       stretch=0)
## Starting curve---distance^2: 84204082853
## Iteration 1---distance^2: 46039577
## Iteration 2---distance^2: 42955956
## Iteration 3---distance^2: 41286356
## Iteration 4---distance^2: 40749859
## Iteration 5---distance^2: 40679459
## Iteration 6---distance^2: 40714117
#Pseudotime score
PseudotimeScore <- fit$lambda/max(fit$lambda)

if (cor(PseudotimeScore, Neurons.data@assays$SCT@data['Hmga2', ]) > 0) {
  Neurons.data$PseudotimeScore <- -(PseudotimeScore - max(PseudotimeScore))
}

cols <- brewer.pal(n =11, name = "Spectral")

ggplot(Neurons.data@meta.data, aes(Spring_1, Spring_2)) +
  geom_point(aes(color=PseudotimeScore), size=2, shape=16) + 
  scale_color_gradientn(colours=rev(cols), name='Pseudotime score')

# Late Neurons diversity

Extract late neurons

Neurons.data$Cell.state <- cut(Neurons.data$PseudotimeScore,
                              c(0,0.4,0.8,1),
                              include.lowest = T,
                              labels=c("BP","EN","LN"))
DimPlot(Neurons.data,
        group.by = "Cell.state",
        reduction = "spring",
        cols = c("#ebcb2e", "#9ec22f", "#a9961b"),
        pt.size = 1.5) & NoAxes()

LN.data <- subset(Neurons.data, subset = Cell.state == "LN")
DimPlot(LN.data,
        reduction = "spring",
        cols = c("#ebcb2e", "#9ec22f", "#a9961b"),
        pt.size = 1.5) & NoAxes()

## Prepare the dataset for clustering with scrattch.hicat

Gene filtering

# Exclude genes detected in less than 3 cells
num.cells <- Matrix::rowSums(LN.data@assays[["RNA"]]@counts > 0)
genes.use <- names(x = num.cells[which(x = num.cells >= 3)])

GenesToRemove <- c(grep(pattern = "(^Rpl|^Rps|^Mrp)", x = genes.use, value = TRUE),
                   grep(pattern = "^mt-", x = genes.use, value = TRUE),
                   "Xist")

genes.use <- genes.use[!genes.use %in% GenesToRemove]

Normalization

dgeMatrix_count <- as.matrix(LN.data@assays[["RNA"]]@counts)[rownames(LN.data@assays[["RNA"]]@counts) %in% genes.use,]
dgeMatrix_cpm <- cpm(dgeMatrix_count)
norm.dat <- log2(dgeMatrix_cpm + 1)

norm.dat <- Matrix(norm.dat, sparse = TRUE)
Data.matrix <- list(raw.dat=dgeMatrix_count, norm.dat=norm.dat)
attach(Data.matrix)

Exclude unwanted sources of variation

gene.counts <- log2(colSums(as.matrix(Data.matrix$norm.dat) > 0))
nUMI <- log2(colSums(Data.matrix$raw.dat))
perctMito <- LN.data$percent.mito
perctRibo <- LN.data$percent.ribo
Pseudotime <- LN.data$PseudotimeScore

rm.eigen <- as.matrix(cbind(gene.counts,
                            nUMI,
                            perctMito,
                            perctRibo,
                            Pseudotime))

row.names(rm.eigen) <- names(gene.counts)

colnames(rm.eigen) <- c("log2nGenes",
                        "log2nUMI",
                        "perctMito",
                        "perctRibo",
                        "Pseudotime ")

rm(gene.counts, nUMI, perctMito, perctRibo, Pseudotime)

Iterative clustering

# Parameters for iterative clustering
de.param <- de_param(padj.th     = 0.01, 
                     lfc.th      = 0.9,
                     low.th      = 1, 
                     q1.th       = 0.25, 
                     q2.th       = NULL,
                     q.diff.th   = 0.7,
                     de.score.th = 80,
                     min.cells   = 10)
iter.result <- iter_clust(norm.dat, 
                          counts = raw.dat,
                          dim.method = "pca",
                          max.dim = 15,
                          k.nn = 8,
                          de.param = de.param,
                          rm.eigen = rm.eigen,
                          rm.th = 0.7,
                          vg.padj.th = 0.5,
                          method = "louvain",
                          prefix = "test-iter_clust",
                          verbose = F)
## [1] "test-iter_clust"
##   Finding nearest neighbors...DONE ~ 0.01 s
##   Compute jaccard coefficient between nearest-neighbor sets...DONE ~ 0.016 s
##   Build undirected graph from the weighted links...DONE ~ 0.03 s
##   Run louvain clustering on the graph ...DONE ~ 0.015 s
##   Return a community class
##   -Modularity value: 0.8158831 
##   -Number of clusters: 19[1] "test-iter_clust.1"
## [1] "test-iter_clust.2"
##   Finding nearest neighbors...DONE ~ 0.002 s
##   Compute jaccard coefficient between nearest-neighbor sets...DONE ~ 0.008 s
##   Build undirected graph from the weighted links...DONE ~ 0.013 s
##   Run louvain clustering on the graph ...DONE ~ 0.006 s
##   Return a community class
##   -Modularity value: 0.8361195 
##   -Number of clusters: 16[1] "test-iter_clust.3"
##   Finding nearest neighbors...DONE ~ 0.001 s
##   Compute jaccard coefficient between nearest-neighbor sets...DONE ~ 0.008 s
##   Build undirected graph from the weighted links...DONE ~ 0.012 s
##   Run louvain clustering on the graph ...DONE ~ 0.005 s
##   Return a community class
##   -Modularity value: 0.8542279 
##   -Number of clusters: 15
# Merge clusters which are not seperable by DEGs
rd.dat <- t(norm.dat[iter.result$markers,])
merge.result <- merge_cl(norm.dat, 
                         cl = iter.result$cl, 
                         rd.dat = rd.dat,
                         de.param = de.param)

cat(length(unique(merge.result$cl))," Clusters")
## 3  Clusters
LN.data$iter.clust <- merge.result$cl

Idents(LN.data) <- "iter.clust"

colors <-  c("#ebcb2e", "#9ec22f", "#cc3a1b")

DimPlot(LN.data,
        reduction = "spring",
        #cols = colors,
        pt.size = 1.5) & NoAxes()

Neurons.markers <- FindAllMarkers(LN.data,
                                  test.use = "roc",
                                  only.pos = TRUE,
                                  min.pct = 0.25,
                                  logfc.threshold = 0.25)
top10 <- Neurons.markers %>%
          group_by(cluster) %>%
          filter(power > 0.45)

DoHeatmap(LN.data,
          group.colors = c("#ebcb2e", "#9ec22f", "#cc3a1b"),
          features = top10$gene) + NoLegend()

FeaturePlot(object = LN.data,
            features = c("Foxg1", "Zfpm2",
                         "Lhx1", "Zic5", "Zfp503"),
            pt.size = 1,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes() & NoLegend()

## Use fate ID to infer lineages along differentiating cells

Neurons.data$Broadclust.ident <- sapply(Neurons.data$Barcodes,
                              FUN = function(x) {
                                if (x %in% LN.data$Barcodes) {
                                  x = paste0("Neuron_", LN.data@meta.data[x, "iter.clust"])
                                } else {
                                  x = Neurons.data@meta.data[x, "Broadclust.ident"]
                                  }
                              })

Idents(Neurons.data) <- "Broadclust.ident"
DimPlot(Neurons.data,
        reduction = "spring",
        cols = c("#ebcb2e", "#9ec22f", "#a9961b", "#cc3a1b", "#d14c8d", "#4cabdc", "#5ab793", "grey90", "#e7823a", "#046c9a", "#4990c9", "grey60"),
        pt.size = 0.5) & NoAxes()

Run FateID

FateID

Neurons.data <- SCTransform(Neurons.data,
                            method = "glmGamPoi",
                            vars.to.regress = c("percent.mito", "percent.ribo"),
                            verbose = T)
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |==================                                                    |  25%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |====================================================                  |  75%
  |                                                                            
  |======================================================================| 100%
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |==                                                                    |   3%
  |                                                                            
  |=====                                                                 |   7%
  |                                                                            
  |=======                                                               |  10%
  |                                                                            
  |=========                                                             |  13%
  |                                                                            
  |============                                                          |  17%
  |                                                                            
  |==============                                                        |  20%
  |                                                                            
  |================                                                      |  23%
  |                                                                            
  |===================                                                   |  27%
  |                                                                            
  |=====================                                                 |  30%
  |                                                                            
  |=======================                                               |  33%
  |                                                                            
  |==========================                                            |  37%
  |                                                                            
  |============================                                          |  40%
  |                                                                            
  |==============================                                        |  43%
  |                                                                            
  |=================================                                     |  47%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |=====================================                                 |  53%
  |                                                                            
  |========================================                              |  57%
  |                                                                            
  |==========================================                            |  60%
  |                                                                            
  |============================================                          |  63%
  |                                                                            
  |===============================================                       |  67%
  |                                                                            
  |=================================================                     |  70%
  |                                                                            
  |===================================================                   |  73%
  |                                                                            
  |======================================================                |  77%
  |                                                                            
  |========================================================              |  80%
  |                                                                            
  |==========================================================            |  83%
  |                                                                            
  |=============================================================         |  87%
  |                                                                            
  |===============================================================       |  90%
  |                                                                            
  |=================================================================     |  93%
  |                                                                            
  |====================================================================  |  97%
  |                                                                            
  |======================================================================| 100%
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |==                                                                    |   3%
  |                                                                            
  |=====                                                                 |   7%
  |                                                                            
  |=======                                                               |  10%
  |                                                                            
  |=========                                                             |  13%
  |                                                                            
  |============                                                          |  17%
  |                                                                            
  |==============                                                        |  20%
  |                                                                            
  |================                                                      |  23%
  |                                                                            
  |===================                                                   |  27%
  |                                                                            
  |=====================                                                 |  30%
  |                                                                            
  |=======================                                               |  33%
  |                                                                            
  |==========================                                            |  37%
  |                                                                            
  |============================                                          |  40%
  |                                                                            
  |==============================                                        |  43%
  |                                                                            
  |=================================                                     |  47%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |=====================================                                 |  53%
  |                                                                            
  |========================================                              |  57%
  |                                                                            
  |==========================================                            |  60%
  |                                                                            
  |============================================                          |  63%
  |                                                                            
  |===============================================                       |  67%
  |                                                                            
  |=================================================                     |  70%
  |                                                                            
  |===================================================                   |  73%
  |                                                                            
  |======================================================                |  77%
  |                                                                            
  |========================================================              |  80%
  |                                                                            
  |==========================================================            |  83%
  |                                                                            
  |=============================================================         |  87%
  |                                                                            
  |===============================================================       |  90%
  |                                                                            
  |=================================================================     |  93%
  |                                                                            
  |====================================================================  |  97%
  |                                                                            
  |======================================================================| 100%
Neurons.data <- FindVariableFeatures(Neurons.data, selection.method = "vst", nfeatures = 1500)
Norm.Mat <- as.data.frame(as.matrix(Neurons.data@assays$SCT@data[Neurons.data@assays$SCT@var.features,]))

#Rename idents
id <- 4:1
names(id) <- levels(Neurons.data)
Neurons.data <- RenameIdents(Neurons.data, id)

# Set a cluster assignment factor for each cells
ClusterIdent <- Idents(Neurons.data)
names(ClusterIdent) <- names(Idents(Neurons.data))

Attractors <- 1:3

# Distance in spring space
z <- as.matrix(dist(cbind(Neurons.data$Spring_1, Neurons.data$Spring_2)))
Infered.Fate.bias  <- fateBias(Norm.Mat, ClusterIdent, Attractors,
                               z = z,
                               minnr=20,
                               minnrh=30,
                               adapt=TRUE,
                               confidence=0.75,
                               nbfactor=5,
                               use.dist=FALSE,
                               seed=1234,
                               nbtree=NULL)

Inspect test set used iteratively

Neurons.data$FateID.iteration <- "Attractors"
Idents(Neurons.data) <- "FateID.iteration"

for (i in seq(0, length(Infered.Fate.bias$rfl), by = 5)[-1]) {
  iter <- seq(i-4,i)
  Barcodes <- c()
  for (j in iter) {
    Barcodes <- c(Barcodes, names(Infered.Fate.bias$rfl[[j]]$test$predicted))
  }
  Neurons.data <- SetIdent(Neurons.data, cells = Barcodes, value = paste0("iter ",iter[1]," to ", iter[4]))
}

DimPlot(Neurons.data,
        reduction = "spring",
        pt.size = 1) & NoAxes()

Import lineage bias into Seurat meta.data

probs <- Infered.Fate.bias$probs[,seq(length(Attractors))]

Neurons.data$prob.1 <- probs$t1
Neurons.data$prob.2 <- probs$t2
Neurons.data$prob.3 <- probs$t3

FeaturePlot(object = Neurons.data,
            features = c("prob.1", "prob.2", "prob.3"),
            pt.size = 0.5,
            cols = rev(RColorBrewer::brewer.pal(n = 11, name = "Spectral")),
            reduction = "spring",
            order = T) & NoAxes() & NoLegend()

New.data <- data.frame(barcode=Neurons.data$Barcodes,
                       cluster= Neurons.data$Broadclust.ident,
                       spring1= Neurons.data$Spring_1,
                       spring2= Neurons.data$Spring_2,
                       prob.1= Neurons.data$prob.1,
                       prob.2= Neurons.data$prob.2,
                       prob.3 = Neurons.data$prob.3)

New.data$lineage.bias <- colnames(New.data[,5:7])[apply(New.data[,5:7],1,which.max)]

ggplot(New.data, aes(spring1, spring2, colour = lineage.bias)) +
  scale_color_manual(values=c("#e7823a","#cc391b","#026c9a","#d14c8d")) +
  geom_point() 

Transfert ident to the full dataset

Neurons.data$Lineage.bias <- New.data$lineage.bias

Raw.data$Broadclust.ident <- sapply(Raw.data$Barcodes,
                              FUN = function(x) {
                                if (x %in% Neurons.data$Barcodes) {
                                  x = paste0("Neuron_",Neurons.data@meta.data[x, "Lineage.bias"])
                                } else {
                                  x = Raw.data@meta.data[x, "Broadclust.ident"]
                                  }
                              })

Idents(Raw.data) <- "Broadclust.ident"
DimPlot(Raw.data,
        reduction = "spring",
        cols = c("#ebcb2e", "#9ec22f", "#a9961b", "#cc3a1b", "#d14c8d", "#4cabdc", "#5ab793", "#e7823a", "#046c9a", "grey90", "#4990c9"),
        pt.size = 0.5) & NoAxes()

rm(list = ls()[!ls() %in% "Raw.data"])
gc()
##             used   (Mb) gc trigger   (Mb)  max used   (Mb)
## Ncells   3041343  162.5    4657471  248.8   4657471  248.8
## Vcells 252262490 1924.7  712206197 5433.8 699905385 5339.9

Project progenitors domain ident from WT

WT.KO <- list(WT = readRDS("../QC.filtered.clustered.cells.RDS") %>%
                subset(subset = orig.ident == "Hem1" & Cell_ident %in% c("ChP_progenitors", "ChP",
                                                                         "Dorso-Medial_pallium", "Medial_pallium",
                                                                         "Hem", "Thalamic_eminence") ),
              KO = Raw.data %>% subset(idents = c(1,2,3,5)))
p1 <- DimPlot(object = WT.KO[["WT"]],
        group.by = "Cell.state",
        reduction = "spring",
        cols = c("#31b6bd", "#ebcb2e", "#9ec22f", "#cc3a1b", "#d14c8d", "#4cabdc", "#5ab793", "#e7823a", "#046c9a", "#4990c9"),
        pt.size = 1.5
        )  & NoAxes()

p2 <- DimPlot(WT.KO[["KO"]],
        reduction = "spring",
        group.by = "Broadclust.ident",
        cols = c("#ebcb2e", "#9ec22f", "#a9961b", "#cc3a1b"),
        pt.size = 1.5) & NoAxes()

p1 + p2

WT.KO[["WT"]] <- NormalizeData(WT.KO[["WT"]], normalization.method = "LogNormalize", scale.factor = 10000, assay = "RNA")
WT.KO[["KO"]] <- NormalizeData(WT.KO[["KO"]], normalization.method = "LogNormalize", scale.factor = 10000, assay = "RNA")
WT.KO[["WT"]] <- FindVariableFeatures(WT.KO[["WT"]], selection.method = "vst", nfeatures = 2000)
WT.KO[["KO"]] <- FindVariableFeatures(WT.KO[["KO"]], selection.method = "vst", nfeatures = 2000)
features <- SelectIntegrationFeatures(object.list = WT.KO, nfeatures = 1500)

TFs <- read.table("TF.csv", sep = ";")[,1]
TFs <- features[features %in% TFs]

transfert identity labels WT to KO

KO.anchors <- FindTransferAnchors(reference = WT.KO[["WT"]],
                                  query = WT.KO[["KO"]],
                                  features = TFs,
                                  reduction = "rpca",
                                  k.anchor = 5,
                                  k.filter = 100,
                                  k.score = 30,
                                  npcs = 25,
                                  dims = 1:25,
                                  max.features = 200)

predictions <- TransferData(anchorset = KO.anchors,
                            refdata = WT.KO[["WT"]]$Cell.state,
                            dims = 1:25)

WT.KO[["KO"]] <- AddMetaData(WT.KO[["KO"]], metadata = predictions)
cols <- brewer.pal(n =11, name = "Spectral")

ggplot(WT.KO[["KO"]]@meta.data, aes(Spring_1, Spring_2)) +
  geom_point(aes(color=prediction.score.max), size=1, shape=16) + 
  scale_color_gradientn(colours=rev(cols), name='prediction.score.max')

p1 <- DimPlot(object = WT.KO[["WT"]],
        group.by = "Cell.state",
        reduction = "spring",
        cols = c("#7293c8", "#b79f0b", "#3ca73f","#31b6bd",
                 "#ebcb2e", "#9ec22f", "#a9961b", "#cc3a1b",
                 "#d14c8d", "#4cabdc", "#5ab793", "#e7823a",
                 "#046c9a", "#4990c9"),
        pt.size = 1)  & NoAxes()

p2 <- DimPlot(WT.KO[["KO"]],
              group.by = "predicted.id",
              reduction = "spring",
              cols = c("#31b6bd", "#ebcb2e", "#9ec22f", "#a9961b", "#cc3a1b"),
              pt.size = 1) & NoAxes()

p1 + p2

Transfert to the full dataset

Raw.data$Cell.ident <- sapply(Raw.data$Barcodes,
                              FUN = function(x) {
                                if (x %in% WT.KO[["KO"]]$Barcodes) {
                                  x = WT.KO[["KO"]]@meta.data[x, "predicted.id"]
                                } else {
                                  x = Raw.data@meta.data[x, "Broadclust.ident"]
                                  }
                              })
DimPlot(object = Raw.data,
        group.by = "Cell.ident",
        reduction = "spring",
        cols = c( "#4cabdc", "#7293c8", "grey40" ,"#3ca73f","grey80",
                  "#31b6bd", "#ebcb2e", "#9ec22f", "#a9961b",
                 "#046c9a", "#4990c9","#e7823a", "#cc3a1b"),
        pt.size = 0.5)  & NoAxes()

Save the object

saveRDS(Raw.data, "./GmncKO.cells.RDS")

Session Info

#date
format(Sys.time(), "%d %B, %Y, %H,%M")
## [1] "02 mai, 2022, 17,45"
#Packages used
sessionInfo()
## R version 4.1.3 (2022-03-10)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.4 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
## LAPACK: /home/matthieu/anaconda3/lib/libmkl_rt.so.1
## 
## locale:
##  [1] LC_CTYPE=fr_FR.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=fr_FR.UTF-8        LC_COLLATE=fr_FR.UTF-8    
##  [5] LC_MONETARY=fr_FR.UTF-8    LC_MESSAGES=fr_FR.UTF-8   
##  [7] LC_PAPER=fr_FR.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] Rphenograph_0.99.1   igraph_1.2.11        matrixStats_0.61.0  
##  [4] princurve_2.1.6      wesanderson_0.3.6    reticulate_1.22     
##  [7] cowplot_1.1.1        ggExtra_0.9          ggplot2_3.3.5       
## [10] RColorBrewer_1.1-2   dplyr_1.0.7          Matrix_1.4-1        
## [13] FateID_0.2.1         scrattch.hicat_1.0.0 SeuratObject_4.0.4  
## [16] Seurat_4.0.5        
## 
## loaded via a namespace (and not attached):
##   [1] Rtsne_0.15            colorspace_2.0-2      deldir_1.0-6         
##   [4] ellipsis_0.3.2        ggridges_0.5.3        som_0.3-5.1          
##   [7] spatstat.data_2.1-0   farver_2.1.0          leiden_0.3.9         
##  [10] listenv_0.8.0         ggrepel_0.9.1         lle_1.1              
##  [13] RSpectra_0.16-0       fansi_0.5.0           codetools_0.2-18     
##  [16] splines_4.1.3         knitr_1.36            polyclip_1.10-0      
##  [19] jsonlite_1.7.2        umap_0.2.8.0          ica_1.0-2            
##  [22] cluster_2.1.3         png_0.1-7             pheatmap_1.0.12      
##  [25] snowfall_1.84-6.1     uwot_0.1.10           shiny_1.7.1          
##  [28] sctransform_0.3.2     spatstat.sparse_2.0-0 compiler_4.1.3       
##  [31] httr_1.4.2            assertthat_0.2.1      fastmap_1.1.0        
##  [34] lazyeval_0.2.2        limma_3.50.0          later_1.3.0          
##  [37] htmltools_0.5.2       tools_4.1.3           gtable_0.3.0         
##  [40] glue_1.5.1            RANN_2.6.1            reshape2_1.4.4       
##  [43] Rcpp_1.0.8            scattermore_0.7       jquerylib_0.1.4      
##  [46] vctrs_0.3.8           nlme_3.1-153          lmtest_0.9-39        
##  [49] xfun_0.28             stringr_1.4.0         globals_0.14.0       
##  [52] mime_0.12             miniUI_0.1.1.1        lifecycle_1.0.1      
##  [55] irlba_2.3.3           goftest_1.2-3         future_1.23.0        
##  [58] MASS_7.3-56           zoo_1.8-9             scales_1.1.1         
##  [61] spatstat.core_2.3-1   promises_1.2.0.1      spatstat.utils_2.2-0 
##  [64] parallel_4.1.3        yaml_2.2.1            pbapply_1.5-0        
##  [67] gridExtra_2.3         sass_0.4.0            rpart_4.1.16         
##  [70] stringi_1.7.6         highr_0.9             randomForest_4.7-1   
##  [73] rlang_0.4.12          pkgconfig_2.0.3       evaluate_0.14        
##  [76] lattice_0.20-45       ROCR_1.0-11           purrr_0.3.4          
##  [79] tensor_1.5            labeling_0.4.2        patchwork_1.1.1      
##  [82] htmlwidgets_1.5.4     tidyselect_1.1.1      parallelly_1.29.0    
##  [85] RcppAnnoy_0.0.19      plyr_1.8.6            magrittr_2.0.2       
##  [88] R6_2.5.1              generics_0.1.1        DBI_1.1.1            
##  [91] withr_2.4.3           pillar_1.6.4          mgcv_1.8-40          
##  [94] fitdistrplus_1.1-6    scatterplot3d_0.3-41  survival_3.2-13      
##  [97] abind_1.4-5           tibble_3.1.6          future.apply_1.8.1   
## [100] crayon_1.4.2          KernSmooth_2.23-20    utf8_1.2.2           
## [103] spatstat.geom_2.3-0   plotly_4.10.0         rmarkdown_2.11       
## [106] locfit_1.5-9.4        grid_4.1.3            data.table_1.14.2    
## [109] digest_0.6.29         xtable_1.8-4          tidyr_1.1.4          
## [112] httpuv_1.6.3          openssl_1.4.5         munsell_0.5.0        
## [115] viridisLite_0.4.0     bslib_0.3.1           askpass_1.1

  1. Institute of Psychiatry and Neuroscience of Paris, INSERM U1266, 75014, Paris, France, ↩︎

LS0tCnRpdGxlOiAiR21uYyBLTyBxdWFsaXR5IGNvbnRyb2wiCmF1dGhvcjoKICAgLSBNYXR0aGlldSBNb3JlYXVeW0luc3RpdHV0ZSBvZiBQc3ljaGlhdHJ5IGFuZCBOZXVyb3NjaWVuY2Ugb2YgUGFyaXMsIElOU0VSTSBVMTI2NiwgNzUwMTQsIFBhcmlzLCBGcmFuY2UsIG1hdHRoaWV1Lm1vcmVhdUBpbnNlcm0uZnJdIFshW10oaHR0cHM6Ly9vcmNpZC5vcmcvc2l0ZXMvZGVmYXVsdC9maWxlcy9pbWFnZXMvb3JjaWRfMTZ4MTYucG5nKV0oaHR0cHM6Ly9vcmNpZC5vcmcvMDAwMC0wMDAyLTI1OTItMjM3MykKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6IAogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBkZl9wcmludDogdGliYmxlCiAgICBoaWdobGlnaHQ6IGhhZGRvY2sKICAgIHRoZW1lOiBjb3NtbwogICAgY3NzOiAiLi4vc3R5bGUuY3NzIgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHllcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGZpZy5hbGlnbiA9ICdjZW50ZXInLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZS5sYXp5ID0gRkFMU0UpCmBgYAoKIyBMb2FkIGxpYnJhcmllcwoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoc2NyYXR0Y2guaGljYXQpCmxpYnJhcnkoRmF0ZUlEKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShkcGx5cikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ0V4dHJhKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkocmV0aWN1bGF0ZSkKbGlicmFyeSh3ZXNhbmRlcnNvbikKbGlicmFyeShwcmluY3VydmUpCnVzZV9weXRob24oIi91c3IvYmluL3B5dGhvbjMiKQoKI1NldCBnZ3Bsb3QgdGhlbWUgYXMgY2xhc3NpYwp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQpgYGAKCiMgTG9hZCB0aGUgcmF3IGNvdW50cyBtYXRyaXgKCmBgYHtyfQpDb3VudGRhdGEgPC0gUmVhZDEwWCgiLi4vLi4vUmF3RGF0YS9HbW5jX0tPL291dHMvZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXgvIikKClJhdy5kYXRhIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBDb3VudGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3QgPSAiR21uY19LTyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscyA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5mZWF0dXJlcyA9IDgwMCkKClJhdy5kYXRhJEJhcmNvZGVzIDwtIHJvd25hbWVzKFJhdy5kYXRhQG1ldGEuZGF0YSkKCnJtKENvdW50ZGF0YSkKCmRpbShSYXcuZGF0YSkKYGBgCmBgYHtyfQpSYXcuZGF0YSRwZXJjZW50Lm1pdG8gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoUmF3LmRhdGEsIHBhdHRlcm4gPSAiXm10LSIpClJhdy5kYXRhJHBlcmNlbnQucmlibyA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChSYXcuZGF0YSwgcGF0dGVybiA9ICIoXlJwbHxeUnBzfF5NcnApIikKYGBgCgpgYGB7cn0KVmxuUGxvdChvYmplY3QgPSBSYXcuZGF0YSwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCJuQ291bnRfUk5BIiwgInBlcmNlbnQubWl0byIsICJwZXJjZW50LnJpYm8iKSwgbmNvbD0gMikgJiBOb0F4ZXMoKQpgYGAKIyBJbnNwZWN0IGNlbGwgYmFzZWQgb24gcmVsYXRpb24gYmV0d2VlbiBuVU1JIGFuZCBuR2VuZSBkZXRlY3RlZAoKYGBge3J9CiMgUmVsYXRpb24gYmV0d2VlbiBuVU1JIGFuZCBuR2VuZSBkZXRlY3RlZApDZWxsLlFDLlN0YXQgPC0gUmF3LmRhdGFAbWV0YS5kYXRhCgpwMSA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD1uQ291bnRfUk5BLCB5PW5GZWF0dXJlX1JOQSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCnAxIDwtIGdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKCnAyIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyh4PWxvZzEwKG5Db3VudF9STkEpLCB5PWxvZzEwKG5GZWF0dXJlX1JOQSkpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKQpwMiA8LSBnZ01hcmdpbmFsKHAyLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGw9ImxpZ2h0Z3JleSIpCgpwbG90X2dyaWQocGxvdGxpc3QgPSBsaXN0KHAxLHAyKSwgbmNvbD0yLCBhbGlnbj0naCcsIHJlbF93aWR0aHMgPSBjKDEsIDEpKSA7IHJtKHAxLHAyKQpgYGAKCkNlbGxzIHdpdGggZGV2aWF0aW5nIG5HZW5lL25VTUkgcmF0aW8gZGlzcGxheSBhbiBFcnl0aHJvY3l0ZSBzaWduYXR1cmUgCgoKYGBge3J9ClJhdy5kYXRhIDwtIEFkZE1vZHVsZVNjb3JlKFJhdy5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGxpc3QoYygiSGJiLWJ0IiwgIkhicTFhIiwgIklzZzIwIiwgIkZlY2giLCAiU25jYSIsICJSZWMxMTQiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGN0cmwgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJFcnl0aHJvY3l0ZS5zaWduYXR1cmUiKQoKQ2VsbC5RQy5TdGF0JEVyeXRocm9jeXRlLnNpZ25hdHVyZSA8LSBSYXcuZGF0YSRFcnl0aHJvY3l0ZS5zaWduYXR1cmUxCmBgYAoKYGBge3J9CmdyYWRpZW50IDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbChuID0xMSwgbmFtZSA9ICJTcGVjdHJhbCIpKSgxMDApCgpwMSA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMobG9nMTAobkNvdW50X1JOQSksIHk9bG9nMTAobkZlYXR1cmVfUk5BKSkpICsKICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IEVyeXRocm9jeXRlLnNpZ25hdHVyZSkpICArIAogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3Vycz1yZXYoZ3JhZGllbnQpLCBuYW1lPSdFcnl0aHJvY3l0ZSBzY29yZScpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKCnAyIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyhsb2cxMChuQ291bnRfUk5BKSwgeT1sb2cxMChuRmVhdHVyZV9STkEpKSkgKwogICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gcGVyY2VudC5taXRvKSkgICsgCiAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzPXJldihncmFkaWVudCksIG5hbWU9J1BlcmNlbnQgbWl0bycpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKCnAzIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyhsb2cxMChuQ291bnRfUk5BKSwgeT1sb2cxMChuRmVhdHVyZV9STkEpKSkgKwogICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gcGVyY2VudC5yaWJvKSkgICsgCiAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzPXJldihncmFkaWVudCksIG5hbWU9J1BlcmNlbnQgcmlibycpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKCnAxICsgcDIgKyBwMwpgYGAKIyMgRXhjbHVkZSBFcnl0aHJvY3l0ZXMKCmBgYHtyfQpDZWxsLlFDLlN0YXQkRXJ5dGhyb2N5dGUgPC0gaWZlbHNlKENlbGwuUUMuU3RhdCRFcnl0aHJvY3l0ZS5zaWduYXR1cmUgPiAwLjEsICJFcnl0aHJvY3l0ZSIsICJOb3RfRXJ5dGhyb2N5dGUiKQpgYGAKCmBgYHtyfQpwMiA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD1sb2cxMChuQ291bnRfUk5BKSwgeT1sb2cxMChuRmVhdHVyZV9STkEpKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IEVyeXRocm9jeXRlKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCgpnZ01hcmdpbmFsKHAyLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGw9ImxpZ2h0Z3JleSIpCmBgYAoKYGBge3J9CiMgRmlsdGVyIGNlbGxzIGJhc2VkIG9uIHRoZXNlIHRocmVzaG9sZHMKQ2VsbC5RQy5TdGF0IDwtIENlbGwuUUMuU3RhdCAlPiUgZmlsdGVyKENlbGwuUUMuU3RhdCRFcnl0aHJvY3l0ZS5zaWduYXR1cmUgPCAwLjEpCmBgYAoKIyBMb3cgcXVhbGl0eSBjZWxsIGZpbHRlcmluZwoKIyMgRmlsdGVyaW5nIGNlbGxzIGJhc2VkIG9uIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0cwoKV2UgYXBwbGllZCBhIGhpZ2ggYW5kIGxvdyBtZWRpYW4gYWJzb2x1dGUgZGV2aWF0aW9uIChtYWQpIHRocmVzaG9sZHMgdG8gZXhjbHVkZSBvdXRsaWVyIGNlbGxzCgpgYGB7cn0KbWF4Lm1pdG8udGhyIDwtIG1lZGlhbihDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvKSArIDMqbWFkKENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8pCm1pbi5taXRvLnRociA8LSBtZWRpYW4oQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0bykgLSAzKm1hZChDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvKQpgYGAKCmBgYHtyfQpwMSA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD1uRmVhdHVyZV9STkEsIHk9cGVyY2VudC5taXRvKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1heC5taXRvLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWluLm1pdG8udGhyKSwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gMikgKwogIGFubm90YXRlKGdlb20gPSAidGV4dCIsIGxhYmVsID0gcGFzdGUwKGFzLm51bWVyaWModGFibGUoQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0byA+IG1heC5taXRvLnRociB8IENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8gPCBtaW4ubWl0by50aHIpWzJdKSwiIGNlbGxzIHJlbW92ZWRcbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMubnVtZXJpYyh0YWJsZShDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvID4gbWF4Lm1pdG8udGhyIHwgQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0byA8IG1pbi5taXRvLnRocilbMV0pLCIgY2VsbHMgcmVtYWluIiksCiAgICAgICAgICAgeCA9IDYwMDAsIHkgPSAyMCkKCmdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IiwgYmlucz0xMDApIApgYGAKYGBge3J9CiMgRmlsdGVyIGNlbGxzIGJhc2VkIG9uIHRoZXNlIHRocmVzaG9sZHMKQ2VsbC5RQy5TdGF0IDwtIENlbGwuUUMuU3RhdCAlPiUgZmlsdGVyKHBlcmNlbnQubWl0byA8IG1heC5taXRvLnRocikgJT4lIGZpbHRlcihwZXJjZW50Lm1pdG8gPiBtaW4ubWl0by50aHIpCmBgYAoKIyMgRmlsdGVyaW5nIGNlbGxzIGJhc2VkIG9uIG51bWJlciBvZiBnZW5lcyBhbmQgdHJhbnNjcmlwdHMgZGV0ZWN0ZWQKCiMjIyBSZW1vdmUgY2VsbHMgd2l0aCB0byBmZXcgZ2VuZSBkZXRlY3RlZCBvciB3aXRoIHRvIG1hbnkgVU1JIGNvdW50cwoKV2UgZmlsdGVyIGNlbGxzIHdoaWNoIGFyZSBsaWtlbHkgdG8gYmUgZG91YmxldCBiYXNlZCBvbiB0aGVpciBoaWdoZXIgY29udGVudCBvZiB0cmFuc2NyaXB0IGRldGVjdGVkIGFzIHdlbGwgYXMgY2VsbCB3aXRoIHRvIGZldyBnZW5lcy9VTUkgc2VxdWVuY2VkCgpgYGB7cn0KIyBTZXQgbG93IGFuZCBoaWdodCB0aHJlc2hvbGRzIG9uIHRoZSBudW1iZXIgb2YgZGV0ZWN0ZWQgZ2VuZXMgYmFzZWQgb24gdGhlIG9uZSBvYnRhaW4gd2l0aCB0aGUgV1QgZGF0YXNldAptaW4uR2VuZXMudGhyIDwtIGxvZzEwKDE2MzUpCm1heC5HZW5lcy50aHIgPC0gbG9nMTAoODA2OSkKCiMgU2V0IGhpZ2h0IHRocmVzaG9sZCBvbiB0aGUgbnVtYmVyIG9mIHRyYW5zY3JpcHRzCm1heC5uVU1JLnRociA8LSBsb2cxMCg1ODk1OCkKYGBgCgoKYGBge3J9CiMgR2VuZS9VTUkgc2NhdHRlciBwbG90IGJlZm9yZSBmaWx0ZXJpbmcKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAobkNvdW50X1JOQSksIHk9bG9nMTAobkZlYXR1cmVfUk5BKSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1pbi5HZW5lcy50aHIpLCBjb2xvdXIgPSAiZ3JlZW4iLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWF4LkdlbmVzLnRociksIGNvbG91ciA9ICJncmVlbiIsIGxpbmV0eXBlID0gMikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtYXgublVNSS50aHIpLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAyKQoKZ2dNYXJnaW5hbChwMSwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQpgYGAKYGBge3J9CiMgRmlsdGVyIGNlbGxzIGJhc2Ugb24gYm90aCBtZXRyaWNzCkNlbGwuUUMuU3RhdCA8LSBDZWxsLlFDLlN0YXQgJT4lIGZpbHRlcihsb2cxMChuRmVhdHVyZV9STkEpID4gbWluLkdlbmVzLnRocikgJT4lIGZpbHRlcihsb2cxMChuQ291bnRfUk5BKSA8IG1heC5uVU1JLnRocikKYGBgCgojIyMgRmlsdGVyIGNlbGxzIGJlbG93IHRoZSBtYWluIHBvcHVsYXRpb24gblVNSS9uR2VuZSByZWxhdGlvbnNoaXAKCmBgYHtyfQpsbS5tb2RlbCA8LSBsbShkYXRhID0gQ2VsbC5RQy5TdGF0LCBmb3JtdWxhID0gbG9nMTAobkZlYXR1cmVfUk5BKSB+IGxvZzEwKG5Db3VudF9STkEpKQoKcDIgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAobkNvdW50X1JOQSksIHk9bG9nMTAobkZlYXR1cmVfUk5BKSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1pbi5HZW5lcy50aHIpLCBjb2xvdXIgPSAiZ3JlZW4iLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWF4LkdlbmVzLnRociksIGNvbG91ciA9ICJncmVlbiIsIGxpbmV0eXBlID0gMikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtYXgublVNSS50aHIpLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAyKSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSBwYXN0ZTAoZGltKENlbGwuUUMuU3RhdClbMV0sICIgUUMgcGFzc2VkIGNlbGxzIiksIHggPSA0LCB5ID0gMy44KQoKZ2dNYXJnaW5hbChwMiwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQpgYGAKIyMgRmlsdGVyIHRoZSBTZXVyYXQgb2JqZWN0CgpgYGB7cn0KUmF3LmRhdGEgPC0gc3Vic2V0KHggPSBSYXcuZGF0YSwgc3Vic2V0ID0gQmFyY29kZXMgJWluJSAgQ2VsbC5RQy5TdGF0JEJhcmNvZGVzKQpgYGAKCmBgYHtyfQojIFBsb3QgZmluYWwgUUMgbWV0cmljcwpWbG5QbG90KG9iamVjdCA9IFJhdy5kYXRhLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsIm5Db3VudF9STkEiLCAicGVyY2VudC5taXRvIiwgInBlcmNlbnQucmlibyIpLCBuY29sPSAyKSAmIE5vQXhlcygpCmBgYApgYGB7cn0KcDEgPC0gZ2dwbG90KFJhdy5kYXRhQG1ldGEuZGF0YSwgYWVzKHg9bG9nMTAobkNvdW50X1JOQSksIHk9bG9nMTAobkZlYXR1cmVfUk5BKSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCmdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKYGBgCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lICJSYXcuZGF0YSJdKQpgYGAKCgojIFVzZSBTY3J1YmxldCB0byBkZXRlY3Qgb2J2aW91cyBkb3VibGV0cwoKIyMgUnVuIFNjcnVibGV0IHdpdGggZGVmYXVsdCBwYXJhbWV0ZXIKCkV4cG9ydCByYXcgY291bnQgbWF0cml4IGFzIGlucHV0IHRvIFNjcnVibGV0CgpgYGB7cn0KI0V4cG9ydCBmaWx0ZXJlZCBtYXRyaXgKZGlyLmNyZWF0ZSgiLi4vLi4vUmF3RGF0YS9HbW5jX0tPL1NjcnVibGV0X2lucHV0cyIpCgpleHByRGF0YSA8LSBNYXRyaXgoYXMubWF0cml4KFJhdy5kYXRhQGFzc2F5c1tbIlJOQSJdXUBjb3VudHMpLCBzcGFyc2UgPSBUUlVFKQp3cml0ZU1NKGV4cHJEYXRhLCAiLi4vLi4vUmF3RGF0YS9HbW5jX0tPL1NjcnVibGV0X2lucHV0cy9tYXRyaXgxLm10eCIpCmBgYApgYGB7cHl0aG9ufQppbXBvcnQgc2NydWJsZXQgYXMgc2NyCmltcG9ydCBzY2lweS5pbwppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IG9zCgojTG9hZCByYXcgY291bnRzIG1hdHJpeCBhbmQgZ2VuZSBsaXN0CmlucHV0X2RpciA9ICcuLi8uLi9SYXdEYXRhL0dtbmNfS08vU2NydWJsZXRfaW5wdXRzJwpjb3VudHNfbWF0cml4ID0gc2NpcHkuaW8ubW1yZWFkKGlucHV0X2RpciArICcvbWF0cml4MS5tdHgnKS5ULnRvY3NjKCkKCiNJbml0aWFsaXplIFNjcnVibGV0CnNjcnViID0gc2NyLlNjcnVibGV0KGNvdW50c19tYXRyaXgsCiAgICAgICAgICAgICAgICAgICAgIGV4cGVjdGVkX2RvdWJsZXRfcmF0ZT0wLjEsCiAgICAgICAgICAgICAgICAgICAgIHNpbV9kb3VibGV0X3JhdGlvPTIsCiAgICAgICAgICAgICAgICAgICAgIG5fbmVpZ2hib3JzID0gOCkKCiNSdW4gdGhlIGRlZmF1bHQgcGlwZWxpbmUKZG91YmxldF9zY29yZXMsIHByZWRpY3RlZF9kb3VibGV0cyA9IHNjcnViLnNjcnViX2RvdWJsZXRzKG1pbl9jb3VudHM9MSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fY2VsbHM9MywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fZ2VuZV92YXJpYWJpbGl0eV9wY3RsPTg1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fcHJpbl9jb21wcz0yNSkKYGBgCgpgYGB7cn0KIyBJbXBvcnQgc2NydWJsZXQncyBkb3VibGV0IHNjb3JlClJhdy5kYXRhJERvdWJsZXRzY29yZSA8LSBweSRkb3VibGV0X3Njb3JlcwoKIyBQbG90IGRvdWJsZXQgc2NvcmUKZ2dwbG90KFJhdy5kYXRhQG1ldGEuZGF0YSwgYWVzKHggPSBEb3VibGV0c2NvcmUsIHN0YXQobmRlbnNpdHkpKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyMDAsIGNvbG91ciA9ImxpZ2h0Z3JleSIpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAuMTUsIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpCmBgYApgYGB7cn0KIyBNYW51YWxseSBzZXQgdGhyZXNob2xkIGF0IGRvdWJsZXQgc2NvcmUgdG8gMC4yClJhdy5kYXRhJFByZWRpY3RlZF9kb3VibGV0cyA8LSBpZmVsc2UocHkkZG91YmxldF9zY29yZXMgPiAwLjE1LCAiRG91YmxldCIsIlNpbmdsZXQiKQp0YWJsZShSYXcuZGF0YSRQcmVkaWN0ZWRfZG91YmxldHMpCmBgYApgYGB7cn0KUmF3LmRhdGEgPC0gc3Vic2V0KHggPSBSYXcuZGF0YSwgc3Vic2V0ID0gUHJlZGljdGVkX2RvdWJsZXRzID09ICJTaW5nbGV0IikKYGBgCgojIEdlbmVyYXRlIFNSSU5HIGRpbWVudGlvbmFsaXR5IHJlZHVjdGlvbgoKIyMgRXhwb3J0IGNvdW50cyBtYXRyaXgKCmBgYHtyfQpkaXIuY3JlYXRlKCIuL1NwcmluZ0Nvb3JkaW5hdGVzIikKYGBgCgpgYGB7cn0KIyBFeHBvcnQgcmF3IGV4cHJlc3Npb24gbWF0cml4IGFuZCBnZW5lIGxpc3QgdG8gcmVnZW5lcmF0ZSBhIHNwcmluZyBwbG90CmV4cHJEYXRhIDwtIE1hdHJpeChhcy5tYXRyaXgoUmF3LmRhdGFAYXNzYXlzW1siUk5BIl1dQGNvdW50cyksIHNwYXJzZSA9IFRSVUUpCndyaXRlTU0oZXhwckRhdGEsICIuL1NwcmluZ0Nvb3JkaW5hdGVzL0V4cHJEYXRhLm10eCIpCmBgYAoKYGBge3J9CkdlbmVsaXN0IDwtIHJvdy5uYW1lcyhSYXcuZGF0YUBhc3NheXNbWyJSTkEiXV1AY291bnRzKQp3cml0ZS50YWJsZShHZW5lbGlzdCwgIi4vU3ByaW5nQ29vcmRpbmF0ZXMvR2VuZWxpc3QuY3N2Iiwgc2VwPSJcdCIsIGNvbC5uYW1lcyA9IEYsIHJvdy5uYW1lcyA9IEYsIHF1b3RlID0gRikKYGBgCgpgYGB7cn0KI0V4cG9ydCBtZXRhZGF0YQpTY3J1YmxldCA8LSBjKCJTY3J1YmxldCIsIFJhdy5kYXRhJFByZWRpY3RlZF9kb3VibGV0cykKU2NydWJsZXQgPC0gcGFzdGUoU2NydWJsZXQsIHNlcD0iLCIsIGNvbGxhcHNlPSIsIikKCkNlbGxncm91cGluZyA8LSBTY3J1YmxldAp3cml0ZS50YWJsZShDZWxsZ3JvdXBpbmcsICIuL1NwcmluZ0Nvb3JkaW5hdGVzL0NlbGxncm91cGluZy5jc3YiLCBxdW90ZSA9Riwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gRikKYGBgCgojIyBJbXBvcnQgY29vcmRpbmF0ZXMKCmBgYHtyfQpzcHJpbmcuY29vciA8LSByZWFkLnRhYmxlKCJTcHJpbmdDb29yZGluYXRlcy9jb29yZGluYXRlcy50eHQiLCBzZXAgPSAiLCIsIGhlYWRlciA9IEYsIHJvdy5uYW1lcyA9IDEpCmNvbG5hbWVzKHNwcmluZy5jb29yKSA8LSBjKCJTcHJpbmdfMSIsICJTcHJpbmdfMiIpCmBgYAoKYGBge3J9ClNwcmluZy5TeW0gPC0gZnVuY3Rpb24oeCl7CiAgeCA9IGFicyhtYXgoc3ByaW5nLmNvb3IkU3ByaW5nXzIpLXgpCiB9CgpzcHJpbmcuY29vciRTcHJpbmdfMiA8LSBzYXBwbHkoc3ByaW5nLmNvb3IkU3ByaW5nXzIsIGZ1bmN0aW9uKHgpIFNwcmluZy5TeW0oeCkpCmBgYAoKYGBge3J9ClJhdy5kYXRhJFNwcmluZ18xIDwtIHNwcmluZy5jb29yJFNwcmluZ18xClJhdy5kYXRhJFNwcmluZ18yIDwtIHNwcmluZy5jb29yJFNwcmluZ18yCmBgYAoKCmBgYHtyfQpzcHJpbmcgPC0gYXMubWF0cml4KFJhdy5kYXRhQG1ldGEuZGF0YSAlPiUgc2VsZWN0KCJTcHJpbmdfMSIsICJTcHJpbmdfMiIpKQogIApSYXcuZGF0YVtbInNwcmluZyJdXSA8LSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0gc3ByaW5nLCBrZXkgPSAiU3ByaW5nXyIsIGFzc2F5ID0gRGVmYXVsdEFzc2F5KFJhdy5kYXRhKSkKYGBgCgpgYGB7cn0KRGltUGxvdChSYXcuZGF0YSwgCiAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgcHQuc2l6ZSA9IDAuNSkgJiBOb0F4ZXMoKQpgYGAKIyBCcm9hZCBjbHVzdGVyaW5nCgojIyBTY3RyYW5zZm9ybSBub3JtYWxpemF0aW9uCgpgYGB7ciBjbGFzcy5vdXRwdXQ9InNjcm9sbC0xMDAiLCBjYWNoZT1UUlVFfQpSYXcuZGF0YSA8LSBTQ1RyYW5zZm9ybShSYXcuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdsbUdhbVBvaSIsCiAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcyA9IGMoInBlcmNlbnQubWl0byIpLAogICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVCkKYGBgCiMjIFJ1biBQQ0EgYW5kIGJyb2FkIGNsdXN0ZXJpbmcKCmBgYHtyIGNsYXNzLm91dHB1dD0ic2Nyb2xsLTEwMCIsIGNhY2hlPVRSVUV9ClJhdy5kYXRhIDwtIFJ1blBDQShSYXcuZGF0YSwgdmVyYm9zZSA9IEZBTFNFKQoKUmF3LmRhdGEgPC0gRmluZE5laWdoYm9ycyhSYXcuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMToyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICBrLnBhcmFtID0gOCkKClJhdy5kYXRhIDwtIEZpbmRDbHVzdGVycyhSYXcuZGF0YSwgcmVzb2x1dGlvbiA9IDAuMikKYGBgCgpgYGB7cn0KRGltUGxvdChSYXcuZGF0YSwKICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICBjb2xzID0gYygiI2ViY2IyZSIsICIjOWVjMjJmIiwgIiNhOTk2MWIiLCAiI2NjM2ExYiIsICIjZDE0YzhkIiwgIiM0Y2FiZGMiLCAiIzVhYjc5MyIsICIjZTc4MjNhIiwgIiMwNDZjOWEiLCAiIzQ5OTBjOSIpLAogICAgICAgIHB0LnNpemUgPSAwLjUpICYgTm9BeGVzKCkKYGBgCmBgYHtyfQpSYXcuZGF0YSRCcm9hZGNsdXN0LmlkZW50IDwtIFJhdy5kYXRhJHNldXJhdF9jbHVzdGVycwpgYGAKCiMgRGlmZmVyZW50aWF0aW5nIG5ldXJvbnMgc3ViLWNsdXN0ZXJpbmcKCiMjIEV4dHJhY3QgZGlmZmVyZW50aWF0aW5nIG5ldXJvbnMKCmBgYHtyfQpOZXVyb25zLmRhdGEgPC0gIHN1YnNldChSYXcuZGF0YSwgaWRlbnRzID0gMykKCkRpbVBsb3QoTmV1cm9ucy5kYXRhLAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgIGNvbHMgPSAgYygiI2NjM2ExYiIpKSArIE5vQXhlcygpCmBgYAojIyBGaXQgcHNldWRvdGltZQoKYGBge3J9CmZpdCA8LSBwcmluY2lwYWxfY3VydmUoYXMubWF0cml4KE5ldXJvbnMuZGF0YUBtZXRhLmRhdGFbLGMoIlNwcmluZ18xIiwgIlNwcmluZ18yIildKSwKICAgICAgICAgICAgICAgICAgICAgICBzbW9vdGhlcj0nbG93ZXNzJywKICAgICAgICAgICAgICAgICAgICAgICB0cmFjZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgIGYgPSAxLAogICAgICAgICAgICAgICAgICAgICAgIHN0cmV0Y2g9MCkKYGBgCgpgYGB7cn0KI1BzZXVkb3RpbWUgc2NvcmUKUHNldWRvdGltZVNjb3JlIDwtIGZpdCRsYW1iZGEvbWF4KGZpdCRsYW1iZGEpCgppZiAoY29yKFBzZXVkb3RpbWVTY29yZSwgTmV1cm9ucy5kYXRhQGFzc2F5cyRTQ1RAZGF0YVsnSG1nYTInLCBdKSA+IDApIHsKICBOZXVyb25zLmRhdGEkUHNldWRvdGltZVNjb3JlIDwtIC0oUHNldWRvdGltZVNjb3JlIC0gbWF4KFBzZXVkb3RpbWVTY29yZSkpCn0KCmNvbHMgPC0gYnJld2VyLnBhbChuID0xMSwgbmFtZSA9ICJTcGVjdHJhbCIpCgpnZ3Bsb3QoTmV1cm9ucy5kYXRhQG1ldGEuZGF0YSwgYWVzKFNwcmluZ18xLCBTcHJpbmdfMikpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj1Qc2V1ZG90aW1lU2NvcmUpLCBzaXplPTIsIHNoYXBlPTE2KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzPXJldihjb2xzKSwgbmFtZT0nUHNldWRvdGltZSBzY29yZScpCmBgYAojIExhdGUgTmV1cm9ucyBkaXZlcnNpdHkKCiMjIEV4dHJhY3QgbGF0ZSBuZXVyb25zCgpgYGB7cn0KTmV1cm9ucy5kYXRhJENlbGwuc3RhdGUgPC0gY3V0KE5ldXJvbnMuZGF0YSRQc2V1ZG90aW1lU2NvcmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoMCwwLjQsMC44LDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJCUCIsIkVOIiwiTE4iKSkKYGBgCgpgYGB7cn0KRGltUGxvdChOZXVyb25zLmRhdGEsCiAgICAgICAgZ3JvdXAuYnkgPSAiQ2VsbC5zdGF0ZSIsCiAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgY29scyA9IGMoIiNlYmNiMmUiLCAiIzllYzIyZiIsICIjYTk5NjFiIiksCiAgICAgICAgcHQuc2l6ZSA9IDEuNSkgJiBOb0F4ZXMoKQpgYGAKYGBge3J9CkxOLmRhdGEgPC0gc3Vic2V0KE5ldXJvbnMuZGF0YSwgc3Vic2V0ID0gQ2VsbC5zdGF0ZSA9PSAiTE4iKQpgYGAKCmBgYHtyfQpEaW1QbG90KExOLmRhdGEsCiAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgY29scyA9IGMoIiNlYmNiMmUiLCAiIzllYzIyZiIsICIjYTk5NjFiIiksCiAgICAgICAgcHQuc2l6ZSA9IDEuNSkgJiBOb0F4ZXMoKQpgYGAKIyMgUHJlcGFyZSB0aGUgZGF0YXNldCBmb3IgY2x1c3RlcmluZyB3aXRoIHNjcmF0dGNoLmhpY2F0CgojIyMgR2VuZSBmaWx0ZXJpbmcKCmBgYHtyfQojIEV4Y2x1ZGUgZ2VuZXMgZGV0ZWN0ZWQgaW4gbGVzcyB0aGFuIDMgY2VsbHMKbnVtLmNlbGxzIDwtIE1hdHJpeDo6cm93U3VtcyhMTi5kYXRhQGFzc2F5c1tbIlJOQSJdXUBjb3VudHMgPiAwKQpnZW5lcy51c2UgPC0gbmFtZXMoeCA9IG51bS5jZWxsc1t3aGljaCh4ID0gbnVtLmNlbGxzID49IDMpXSkKCkdlbmVzVG9SZW1vdmUgPC0gYyhncmVwKHBhdHRlcm4gPSAiKF5ScGx8XlJwc3xeTXJwKSIsIHggPSBnZW5lcy51c2UsIHZhbHVlID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICBncmVwKHBhdHRlcm4gPSAiXm10LSIsIHggPSBnZW5lcy51c2UsIHZhbHVlID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAiWGlzdCIpCgpnZW5lcy51c2UgPC0gZ2VuZXMudXNlWyFnZW5lcy51c2UgJWluJSBHZW5lc1RvUmVtb3ZlXQpgYGAKCiMjIyBOb3JtYWxpemF0aW9uCgpgYGB7cn0KZGdlTWF0cml4X2NvdW50IDwtIGFzLm1hdHJpeChMTi5kYXRhQGFzc2F5c1tbIlJOQSJdXUBjb3VudHMpW3Jvd25hbWVzKExOLmRhdGFAYXNzYXlzW1siUk5BIl1dQGNvdW50cykgJWluJSBnZW5lcy51c2UsXQpkZ2VNYXRyaXhfY3BtIDwtIGNwbShkZ2VNYXRyaXhfY291bnQpCm5vcm0uZGF0IDwtIGxvZzIoZGdlTWF0cml4X2NwbSArIDEpCgpub3JtLmRhdCA8LSBNYXRyaXgobm9ybS5kYXQsIHNwYXJzZSA9IFRSVUUpCkRhdGEubWF0cml4IDwtIGxpc3QocmF3LmRhdD1kZ2VNYXRyaXhfY291bnQsIG5vcm0uZGF0PW5vcm0uZGF0KQphdHRhY2goRGF0YS5tYXRyaXgpCmBgYAoKIyMjIEV4Y2x1ZGUgdW53YW50ZWQgc291cmNlcyBvZiB2YXJpYXRpb24KCmBgYHtyfQpnZW5lLmNvdW50cyA8LSBsb2cyKGNvbFN1bXMoYXMubWF0cml4KERhdGEubWF0cml4JG5vcm0uZGF0KSA+IDApKQpuVU1JIDwtIGxvZzIoY29sU3VtcyhEYXRhLm1hdHJpeCRyYXcuZGF0KSkKcGVyY3RNaXRvIDwtIExOLmRhdGEkcGVyY2VudC5taXRvCnBlcmN0UmlibyA8LSBMTi5kYXRhJHBlcmNlbnQucmlibwpQc2V1ZG90aW1lIDwtIExOLmRhdGEkUHNldWRvdGltZVNjb3JlCgpybS5laWdlbiA8LSBhcy5tYXRyaXgoY2JpbmQoZ2VuZS5jb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuVU1JLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVyY3RNaXRvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVyY3RSaWJvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgUHNldWRvdGltZSkpCgpyb3cubmFtZXMocm0uZWlnZW4pIDwtIG5hbWVzKGdlbmUuY291bnRzKQoKY29sbmFtZXMocm0uZWlnZW4pIDwtIGMoImxvZzJuR2VuZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAibG9nMm5VTUkiLAogICAgICAgICAgICAgICAgICAgICAgICAicGVyY3RNaXRvIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBlcmN0UmlibyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJQc2V1ZG90aW1lICIpCgpybShnZW5lLmNvdW50cywgblVNSSwgcGVyY3RNaXRvLCBwZXJjdFJpYm8sIFBzZXVkb3RpbWUpCmBgYAoKIyMgSXRlcmF0aXZlIGNsdXN0ZXJpbmcKCmBgYHtyfQojIFBhcmFtZXRlcnMgZm9yIGl0ZXJhdGl2ZSBjbHVzdGVyaW5nCmRlLnBhcmFtIDwtIGRlX3BhcmFtKHBhZGoudGggICAgID0gMC4wMSwgCiAgICAgICAgICAgICAgICAgICAgIGxmYy50aCAgICAgID0gMC45LAogICAgICAgICAgICAgICAgICAgICBsb3cudGggICAgICA9IDEsIAogICAgICAgICAgICAgICAgICAgICBxMS50aCAgICAgICA9IDAuMjUsIAogICAgICAgICAgICAgICAgICAgICBxMi50aCAgICAgICA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgIHEuZGlmZi50aCAgID0gMC43LAogICAgICAgICAgICAgICAgICAgICBkZS5zY29yZS50aCA9IDgwLAogICAgICAgICAgICAgICAgICAgICBtaW4uY2VsbHMgICA9IDEwKQpgYGAKCgpgYGB7ciBjbGFzcy5vdXRwdXQ9InNjcm9sbC0xMDAiLCBjYWNoZT1UUlVFfQppdGVyLnJlc3VsdCA8LSBpdGVyX2NsdXN0KG5vcm0uZGF0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICBjb3VudHMgPSByYXcuZGF0LAogICAgICAgICAgICAgICAgICAgICAgICAgIGRpbS5tZXRob2QgPSAicGNhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguZGltID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgay5ubiA9IDgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGUucGFyYW0gPSBkZS5wYXJhbSwKICAgICAgICAgICAgICAgICAgICAgICAgICBybS5laWdlbiA9IHJtLmVpZ2VuLAogICAgICAgICAgICAgICAgICAgICAgICAgIHJtLnRoID0gMC43LAogICAgICAgICAgICAgICAgICAgICAgICAgIHZnLnBhZGoudGggPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImxvdXZhaW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgIHByZWZpeCA9ICJ0ZXN0LWl0ZXJfY2x1c3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGKQpgYGAKCmBgYHtyfQojIE1lcmdlIGNsdXN0ZXJzIHdoaWNoIGFyZSBub3Qgc2VwZXJhYmxlIGJ5IERFR3MKcmQuZGF0IDwtIHQobm9ybS5kYXRbaXRlci5yZXN1bHQkbWFya2VycyxdKQptZXJnZS5yZXN1bHQgPC0gbWVyZ2VfY2wobm9ybS5kYXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgY2wgPSBpdGVyLnJlc3VsdCRjbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICByZC5kYXQgPSByZC5kYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICBkZS5wYXJhbSA9IGRlLnBhcmFtKQoKY2F0KGxlbmd0aCh1bmlxdWUobWVyZ2UucmVzdWx0JGNsKSksIiBDbHVzdGVycyIpCmBgYApgYGB7cn0KTE4uZGF0YSRpdGVyLmNsdXN0IDwtIG1lcmdlLnJlc3VsdCRjbAoKSWRlbnRzKExOLmRhdGEpIDwtICJpdGVyLmNsdXN0IgoKY29sb3JzIDwtICBjKCIjZWJjYjJlIiwgIiM5ZWMyMmYiLCAiI2NjM2ExYiIpCgpEaW1QbG90KExOLmRhdGEsCiAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgI2NvbHMgPSBjb2xvcnMsCiAgICAgICAgcHQuc2l6ZSA9IDEuNSkgJiBOb0F4ZXMoKQpgYGAKCmBgYHtyIGNsYXNzLm91dHB1dD0ic2Nyb2xsLTEwMCJ9Ck5ldXJvbnMubWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhMTi5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdC51c2UgPSAicm9jIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ubHkucG9zID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5wY3QgPSAwLjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gMC4yNSkKYGBgCmBgYHtyfQp0b3AxMCA8LSBOZXVyb25zLm1hcmtlcnMgJT4lCiAgICAgICAgICBncm91cF9ieShjbHVzdGVyKSAlPiUKICAgICAgICAgIGZpbHRlcihwb3dlciA+IDAuNDUpCgpEb0hlYXRtYXAoTE4uZGF0YSwKICAgICAgICAgIGdyb3VwLmNvbG9ycyA9IGMoIiNlYmNiMmUiLCAiIzllYzIyZiIsICIjY2MzYTFiIiksCiAgICAgICAgICBmZWF0dXJlcyA9IHRvcDEwJGdlbmUpICsgTm9MZWdlbmQoKQpgYGAKYGBge3J9CkZlYXR1cmVQbG90KG9iamVjdCA9IExOLmRhdGEsCiAgICAgICAgICAgIGZlYXR1cmVzID0gYygiRm94ZzEiLCAiWmZwbTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIkxoeDEiLCAiWmljNSIsICJaZnA1MDMiKSwKICAgICAgICAgICAgcHQuc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCBicmV3ZXIucGFsKDksIllsR25CdSIpKSwKICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKSAmIE5vTGVnZW5kKCkKYGBgCiMjIFVzZSBmYXRlIElEIHRvIGluZmVyIGxpbmVhZ2VzIGFsb25nIGRpZmZlcmVudGlhdGluZyBjZWxscwoKYGBge3J9Ck5ldXJvbnMuZGF0YSRCcm9hZGNsdXN0LmlkZW50IDwtIHNhcHBseShOZXVyb25zLmRhdGEkQmFyY29kZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoeCAlaW4lIExOLmRhdGEkQmFyY29kZXMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBwYXN0ZTAoIk5ldXJvbl8iLCBMTi5kYXRhQG1ldGEuZGF0YVt4LCAiaXRlci5jbHVzdCJdKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gTmV1cm9ucy5kYXRhQG1ldGEuZGF0YVt4LCAiQnJvYWRjbHVzdC5pZGVudCJdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pCgpJZGVudHMoTmV1cm9ucy5kYXRhKSA8LSAiQnJvYWRjbHVzdC5pZGVudCIKYGBgCgpgYGB7cn0KRGltUGxvdChOZXVyb25zLmRhdGEsCiAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgY29scyA9IGMoIiNlYmNiMmUiLCAiIzllYzIyZiIsICIjYTk5NjFiIiwgIiNjYzNhMWIiLCAiI2QxNGM4ZCIsICIjNGNhYmRjIiwgIiM1YWI3OTMiLCAiZ3JleTkwIiwgIiNlNzgyM2EiLCAiIzA0NmM5YSIsICIjNDk5MGM5IiwgImdyZXk2MCIpLAogICAgICAgIHB0LnNpemUgPSAwLjUpICYgTm9BeGVzKCkKYGBgCgojIyBSdW4gRmF0ZUlECgojIyMgRmF0ZUlECgpgYGB7ciBjbGFzcy5vdXRwdXQ9InNjcm9sbC0xMDAiLCBjYWNoZT1UUlVFfQpOZXVyb25zLmRhdGEgPC0gU0NUcmFuc2Zvcm0oTmV1cm9ucy5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdsbUdhbVBvaSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3MgPSBjKCJwZXJjZW50Lm1pdG8iLCAicGVyY2VudC5yaWJvIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVCkKCk5ldXJvbnMuZGF0YSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhOZXVyb25zLmRhdGEsIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IiwgbmZlYXR1cmVzID0gMTUwMCkKYGBgCgpgYGB7cn0KTm9ybS5NYXQgPC0gYXMuZGF0YS5mcmFtZShhcy5tYXRyaXgoTmV1cm9ucy5kYXRhQGFzc2F5cyRTQ1RAZGF0YVtOZXVyb25zLmRhdGFAYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMsXSkpCgojUmVuYW1lIGlkZW50cwppZCA8LSA0OjEKbmFtZXMoaWQpIDwtIGxldmVscyhOZXVyb25zLmRhdGEpCk5ldXJvbnMuZGF0YSA8LSBSZW5hbWVJZGVudHMoTmV1cm9ucy5kYXRhLCBpZCkKCiMgU2V0IGEgY2x1c3RlciBhc3NpZ25tZW50IGZhY3RvciBmb3IgZWFjaCBjZWxscwpDbHVzdGVySWRlbnQgPC0gSWRlbnRzKE5ldXJvbnMuZGF0YSkKbmFtZXMoQ2x1c3RlcklkZW50KSA8LSBuYW1lcyhJZGVudHMoTmV1cm9ucy5kYXRhKSkKCkF0dHJhY3RvcnMgPC0gMTozCgojIERpc3RhbmNlIGluIHNwcmluZyBzcGFjZQp6IDwtIGFzLm1hdHJpeChkaXN0KGNiaW5kKE5ldXJvbnMuZGF0YSRTcHJpbmdfMSwgTmV1cm9ucy5kYXRhJFNwcmluZ18yKSkpCmBgYAoKYGBge3IgY2xhc3Mub3V0cHV0PSJzY3JvbGwtMTAwIiwgY2FjaGU9VFJVRX0KSW5mZXJlZC5GYXRlLmJpYXMgIDwtIGZhdGVCaWFzKE5vcm0uTWF0LCBDbHVzdGVySWRlbnQsIEF0dHJhY3RvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ID0geiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbm5yPTIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlubnJoPTMwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRhcHQ9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZGVuY2U9MC43NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5iZmFjdG9yPTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2UuZGlzdD1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQ9MTIzNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5idHJlZT1OVUxMKQpgYGAKCiMjIyBJbnNwZWN0IHRlc3Qgc2V0IHVzZWQgaXRlcmF0aXZlbHkKCmBgYHtyfQpOZXVyb25zLmRhdGEkRmF0ZUlELml0ZXJhdGlvbiA8LSAiQXR0cmFjdG9ycyIKSWRlbnRzKE5ldXJvbnMuZGF0YSkgPC0gIkZhdGVJRC5pdGVyYXRpb24iCgpmb3IgKGkgaW4gc2VxKDAsIGxlbmd0aChJbmZlcmVkLkZhdGUuYmlhcyRyZmwpLCBieSA9IDUpWy0xXSkgewogIGl0ZXIgPC0gc2VxKGktNCxpKQogIEJhcmNvZGVzIDwtIGMoKQogIGZvciAoaiBpbiBpdGVyKSB7CiAgICBCYXJjb2RlcyA8LSBjKEJhcmNvZGVzLCBuYW1lcyhJbmZlcmVkLkZhdGUuYmlhcyRyZmxbW2pdXSR0ZXN0JHByZWRpY3RlZCkpCiAgfQogIE5ldXJvbnMuZGF0YSA8LSBTZXRJZGVudChOZXVyb25zLmRhdGEsIGNlbGxzID0gQmFyY29kZXMsIHZhbHVlID0gcGFzdGUwKCJpdGVyICIsaXRlclsxXSwiIHRvICIsIGl0ZXJbNF0pKQp9CgpEaW1QbG90KE5ldXJvbnMuZGF0YSwKICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICBwdC5zaXplID0gMSkgJiBOb0F4ZXMoKQpgYGAKCiMjIyBJbXBvcnQgbGluZWFnZSBiaWFzIGludG8gU2V1cmF0IG1ldGEuZGF0YQoKYGBge3J9CnByb2JzIDwtIEluZmVyZWQuRmF0ZS5iaWFzJHByb2JzWyxzZXEobGVuZ3RoKEF0dHJhY3RvcnMpKV0KCk5ldXJvbnMuZGF0YSRwcm9iLjEgPC0gcHJvYnMkdDEKTmV1cm9ucy5kYXRhJHByb2IuMiA8LSBwcm9icyR0MgpOZXVyb25zLmRhdGEkcHJvYi4zIDwtIHByb2JzJHQzCgpGZWF0dXJlUGxvdChvYmplY3QgPSBOZXVyb25zLmRhdGEsCiAgICAgICAgICAgIGZlYXR1cmVzID0gYygicHJvYi4xIiwgInByb2IuMiIsICJwcm9iLjMiKSwKICAgICAgICAgICAgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgY29scyA9IHJldihSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwobiA9IDExLCBuYW1lID0gIlNwZWN0cmFsIikpLAogICAgICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICAgICAgb3JkZXIgPSBUKSAmIE5vQXhlcygpICYgTm9MZWdlbmQoKQpgYGAKYGBge3J9Ck5ldy5kYXRhIDwtIGRhdGEuZnJhbWUoYmFyY29kZT1OZXVyb25zLmRhdGEkQmFyY29kZXMsCiAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcj0gTmV1cm9ucy5kYXRhJEJyb2FkY2x1c3QuaWRlbnQsCiAgICAgICAgICAgICAgICAgICAgICAgc3ByaW5nMT0gTmV1cm9ucy5kYXRhJFNwcmluZ18xLAogICAgICAgICAgICAgICAgICAgICAgIHNwcmluZzI9IE5ldXJvbnMuZGF0YSRTcHJpbmdfMiwKICAgICAgICAgICAgICAgICAgICAgICBwcm9iLjE9IE5ldXJvbnMuZGF0YSRwcm9iLjEsCiAgICAgICAgICAgICAgICAgICAgICAgcHJvYi4yPSBOZXVyb25zLmRhdGEkcHJvYi4yLAogICAgICAgICAgICAgICAgICAgICAgIHByb2IuMyA9IE5ldXJvbnMuZGF0YSRwcm9iLjMpCgpOZXcuZGF0YSRsaW5lYWdlLmJpYXMgPC0gY29sbmFtZXMoTmV3LmRhdGFbLDU6N10pW2FwcGx5KE5ldy5kYXRhWyw1OjddLDEsd2hpY2gubWF4KV0KCmdncGxvdChOZXcuZGF0YSwgYWVzKHNwcmluZzEsIHNwcmluZzIsIGNvbG91ciA9IGxpbmVhZ2UuYmlhcykpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiNlNzgyM2EiLCIjY2MzOTFiIiwiIzAyNmM5YSIsIiNkMTRjOGQiKSkgKwogIGdlb21fcG9pbnQoKSAKYGBgCgojIFRyYW5zZmVydCBpZGVudCB0byB0aGUgZnVsbCBkYXRhc2V0CgpgYGB7cn0KTmV1cm9ucy5kYXRhJExpbmVhZ2UuYmlhcyA8LSBOZXcuZGF0YSRsaW5lYWdlLmJpYXMKClJhdy5kYXRhJEJyb2FkY2x1c3QuaWRlbnQgPC0gc2FwcGx5KFJhdy5kYXRhJEJhcmNvZGVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHggJWluJSBOZXVyb25zLmRhdGEkQmFyY29kZXMpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBwYXN0ZTAoIk5ldXJvbl8iLE5ldXJvbnMuZGF0YUBtZXRhLmRhdGFbeCwgIkxpbmVhZ2UuYmlhcyJdKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gUmF3LmRhdGFAbWV0YS5kYXRhW3gsICJCcm9hZGNsdXN0LmlkZW50Il0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkKCklkZW50cyhSYXcuZGF0YSkgPC0gIkJyb2FkY2x1c3QuaWRlbnQiCmBgYAoKCmBgYHtyfQpEaW1QbG90KFJhdy5kYXRhLAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIGNvbHMgPSBjKCIjZWJjYjJlIiwgIiM5ZWMyMmYiLCAiI2E5OTYxYiIsICIjY2MzYTFiIiwgIiNkMTRjOGQiLCAiIzRjYWJkYyIsICIjNWFiNzkzIiwgIiNlNzgyM2EiLCAiIzA0NmM5YSIsICJncmV5OTAiLCAiIzQ5OTBjOSIpLAogICAgICAgIHB0LnNpemUgPSAwLjUpICYgTm9BeGVzKCkKYGBgCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lICJSYXcuZGF0YSJdKQpnYygpCmBgYAojIFByb2plY3QgcHJvZ2VuaXRvcnMgZG9tYWluIGlkZW50IGZyb20gV1QKCmBgYHtyfQpXVC5LTyA8LSBsaXN0KFdUID0gcmVhZFJEUygiLi4vUUMuZmlsdGVyZWQuY2x1c3RlcmVkLmNlbGxzLlJEUyIpICU+JQogICAgICAgICAgICAgICAgc3Vic2V0KHN1YnNldCA9IG9yaWcuaWRlbnQgPT0gIkhlbTEiICYgQ2VsbF9pZGVudCAlaW4lIGMoIkNoUF9wcm9nZW5pdG9ycyIsICJDaFAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRvcnNvLU1lZGlhbF9wYWxsaXVtIiwgIk1lZGlhbF9wYWxsaXVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIZW0iLCAiVGhhbGFtaWNfZW1pbmVuY2UiKSApLAogICAgICAgICAgICAgIEtPID0gUmF3LmRhdGEgJT4lIHN1YnNldChpZGVudHMgPSBjKDEsMiwzLDUpKSkKCmBgYAoKCmBgYHtyfQpwMSA8LSBEaW1QbG90KG9iamVjdCA9IFdULktPW1siV1QiXV0sCiAgICAgICAgZ3JvdXAuYnkgPSAiQ2VsbC5zdGF0ZSIsCiAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgY29scyA9IGMoIiMzMWI2YmQiLCAiI2ViY2IyZSIsICIjOWVjMjJmIiwgIiNjYzNhMWIiLCAiI2QxNGM4ZCIsICIjNGNhYmRjIiwgIiM1YWI3OTMiLCAiI2U3ODIzYSIsICIjMDQ2YzlhIiwgIiM0OTkwYzkiKSwKICAgICAgICBwdC5zaXplID0gMS41CiAgICAgICAgKSAgJiBOb0F4ZXMoKQoKcDIgPC0gRGltUGxvdChXVC5LT1tbIktPIl1dLAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIGdyb3VwLmJ5ID0gIkJyb2FkY2x1c3QuaWRlbnQiLAogICAgICAgIGNvbHMgPSBjKCIjZWJjYjJlIiwgIiM5ZWMyMmYiLCAiI2E5OTYxYiIsICIjY2MzYTFiIiksCiAgICAgICAgcHQuc2l6ZSA9IDEuNSkgJiBOb0F4ZXMoKQoKcDEgKyBwMgpgYGAKYGBge3J9CldULktPW1siV1QiXV0gPC0gTm9ybWFsaXplRGF0YShXVC5LT1tbIldUIl1dLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCwgYXNzYXkgPSAiUk5BIikKV1QuS09bWyJLTyJdXSA8LSBOb3JtYWxpemVEYXRhKFdULktPW1siS08iXV0sIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIHNjYWxlLmZhY3RvciA9IDEwMDAwLCBhc3NheSA9ICJSTkEiKQpgYGAKCmBgYHtyfQpXVC5LT1tbIldUIl1dIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKFdULktPW1siV1QiXV0sIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IiwgbmZlYXR1cmVzID0gMjAwMCkKV1QuS09bWyJLTyJdXSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhXVC5LT1tbIktPIl1dLCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDApCmBgYAoKYGBge3J9CmZlYXR1cmVzIDwtIFNlbGVjdEludGVncmF0aW9uRmVhdHVyZXMob2JqZWN0Lmxpc3QgPSBXVC5LTywgbmZlYXR1cmVzID0gMTUwMCkKClRGcyA8LSByZWFkLnRhYmxlKCJURi5jc3YiLCBzZXAgPSAiOyIpWywxXQpURnMgPC0gZmVhdHVyZXNbZmVhdHVyZXMgJWluJSBURnNdCmBgYAoKIyMgdHJhbnNmZXJ0IGlkZW50aXR5IGxhYmVscyBXVCB0byBLTwoKYGBge3IgY2xhc3Mub3V0cHV0PSJzY3JvbGwtMTAwIiwgY2FjaGU9VFJVRX0KS08uYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IFdULktPW1siV1QiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeSA9IFdULktPW1siS08iXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IFRGcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJycGNhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGsuYW5jaG9yID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGsuZmlsdGVyID0gMTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgay5zY29yZSA9IDMwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnBjcyA9IDI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltcyA9IDE6MjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguZmVhdHVyZXMgPSAyMDApCgpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gS08uYW5jaG9ycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZmRhdGEgPSBXVC5LT1tbIldUIl1dJENlbGwuc3RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMToyNSkKCldULktPW1siS08iXV0gPC0gQWRkTWV0YURhdGEoV1QuS09bWyJLTyJdXSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKYGBgCmBgYHtyfQpjb2xzIDwtIGJyZXdlci5wYWwobiA9MTEsIG5hbWUgPSAiU3BlY3RyYWwiKQoKZ2dwbG90KFdULktPW1siS08iXV1AbWV0YS5kYXRhLCBhZXMoU3ByaW5nXzEsIFNwcmluZ18yKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPXByZWRpY3Rpb24uc2NvcmUubWF4KSwgc2l6ZT0xLCBzaGFwZT0xNikgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3Vycz1yZXYoY29scyksIG5hbWU9J3ByZWRpY3Rpb24uc2NvcmUubWF4JykKYGBgCgpgYGB7cn0KcDEgPC0gRGltUGxvdChvYmplY3QgPSBXVC5LT1tbIldUIl1dLAogICAgICAgIGdyb3VwLmJ5ID0gIkNlbGwuc3RhdGUiLAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIGNvbHMgPSBjKCIjNzI5M2M4IiwgIiNiNzlmMGIiLCAiIzNjYTczZiIsIiMzMWI2YmQiLAogICAgICAgICAgICAgICAgICIjZWJjYjJlIiwgIiM5ZWMyMmYiLCAiI2E5OTYxYiIsICIjY2MzYTFiIiwKICAgICAgICAgICAgICAgICAiI2QxNGM4ZCIsICIjNGNhYmRjIiwgIiM1YWI3OTMiLCAiI2U3ODIzYSIsCiAgICAgICAgICAgICAgICAgIiMwNDZjOWEiLCAiIzQ5OTBjOSIpLAogICAgICAgIHB0LnNpemUgPSAxKSAgJiBOb0F4ZXMoKQoKcDIgPC0gRGltUGxvdChXVC5LT1tbIktPIl1dLAogICAgICAgICAgICAgIGdyb3VwLmJ5ID0gInByZWRpY3RlZC5pZCIsCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgICAgY29scyA9IGMoIiMzMWI2YmQiLCAiI2ViY2IyZSIsICIjOWVjMjJmIiwgIiNhOTk2MWIiLCAiI2NjM2ExYiIpLAogICAgICAgICAgICAgIHB0LnNpemUgPSAxKSAmIE5vQXhlcygpCgpwMSArIHAyCmBgYAoKIyMgVHJhbnNmZXJ0IHRvIHRoZSBmdWxsIGRhdGFzZXQKCmBgYHtyfQpSYXcuZGF0YSRDZWxsLmlkZW50IDwtIHNhcHBseShSYXcuZGF0YSRCYXJjb2RlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh4ICVpbiUgV1QuS09bWyJLTyJdXSRCYXJjb2RlcykgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IFdULktPW1siS08iXV1AbWV0YS5kYXRhW3gsICJwcmVkaWN0ZWQuaWQiXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gUmF3LmRhdGFAbWV0YS5kYXRhW3gsICJCcm9hZGNsdXN0LmlkZW50Il0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkKYGBgCgoKYGBge3J9CkRpbVBsb3Qob2JqZWN0ID0gUmF3LmRhdGEsCiAgICAgICAgZ3JvdXAuYnkgPSAiQ2VsbC5pZGVudCIsCiAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgY29scyA9IGMoICIjNGNhYmRjIiwgIiM3MjkzYzgiLCAiZ3JleTQwIiAsIiMzY2E3M2YiLCJncmV5ODAiLAogICAgICAgICAgICAgICAgICAiIzMxYjZiZCIsICIjZWJjYjJlIiwgIiM5ZWMyMmYiLCAiI2E5OTYxYiIsCiAgICAgICAgICAgICAgICAgIiMwNDZjOWEiLCAiIzQ5OTBjOSIsIiNlNzgyM2EiLCAiI2NjM2ExYiIpLAogICAgICAgIHB0LnNpemUgPSAwLjUpICAmIE5vQXhlcygpCmBgYAoKIyBTYXZlIHRoZSBvYmplY3QKCmBgYHtyIFNhdmUgUkRTfQpzYXZlUkRTKFJhdy5kYXRhLCAiLi9HbW5jS08uY2VsbHMuUkRTIikKYGBgCgoKIyBTZXNzaW9uIEluZm8KCmBgYHtyfQojZGF0ZQpmb3JtYXQoU3lzLnRpbWUoKSwgIiVkICVCLCAlWSwgJUgsJU0iKQoKI1BhY2thZ2VzIHVzZWQKc2Vzc2lvbkluZm8oKQpgYGA=